from Clases.Pcbt import Pcbt
from Clases.CPU import CPU
from Clases.Signal import Signal
import sys,threading
from Clases.ManagerInterrup import ManagerInterrupt
from Clases.DiscoRigido import DiscoRigido


class Kernel(threading.Thread):
    cpu = None 
    lock = threading.RLock()
    
    def __init__(self, schedule,memoria):
        self.RUNNING = False
        self.memory = memoria #MMU
        self.pcbt = Pcbt()
        self.planificador = schedule# Puede ser un FIFO , PRIORITY o un RR que
                                    #utiliza alguno de los 2 planificadores anteriores
        self.managerInterrupt = ManagerInterrupt()
        self.disco = DiscoRigido()
        
    #getter#
    def getRUNNING(self):
        return self.RUNNING
    
    def getDisco(self):
        return self.disco
    
    
    ## Metodos##
    
    #Este metodo agrega un programa al disco Rigido.
    def agregarProgramaAlDisco(self,pid,programa):
        print("Agrego el programa con ID ",pid, "al disco.")
        self.disco.guardarPrograma(pid, programa)
        
        
    def iniciarProgramas(self):
        print("Se inician los programas (se crean PCBS y se ponen los estados iniciales.)")
        self.lock.acquire() 
        self.managerInterrupt.setKernel(self)
        #El diccionario "disco" al comienzo(antes de hacer el start del kernel) tiene que ser del tipo k =pidActual ^ v=Programa()
        try:
        
            for keyPrograma in self.disco.getDisco(): 
                programa= self.disco.obtenerPrograma(keyPrograma)
                print("agrega el programa con ID: ", programa.getIdPrograma())
                resultadoParaPonerEnDisco=self.memory.inicializarProgramaParaDisco(programa.getIdPrograma(),programa.getListaInstrucciones())
                self.pcbt.agregarPcb(programa,programa.getPrioridad())
                self.planificador.agregarElemetoQReady(self.pcbt.obtenerPcb(programa.getIdPrograma()))# planificador se encarga de la QReady
                self.disco.guardarPrograma(keyPrograma, resultadoParaPonerEnDisco) #Esta linea reemplaza el programa que hay en disco 
                                                                                    # x el tipo de dato que maneje el algoritmo de memoria.
        finally:                                        
            self.lock.release()
             
    def start(self):#Arrancar el sistema
        self.iniciarProgramas()
        
        if not self.getRUNNING():
            self.RUNNING = True
            self.memory.setManager(self.managerInterrupt)
            self.cpu = CPU(self.managerInterrupt, self.memory, self.planificador)
            self.managerInterrupt.interrupt(Signal.NEW)
            self.cpu.start() 
    #Interrupcion STOP #
    def stop(self):
        print("SE EJECUTA EL STOP .... FIN DEL SO")
        self.RUNNING = False
        sys.exit(0) # mata el arbol de procesos 
    #Interrupcion RESCHEDULE #   
    def reschedule(self):
        if self.cpu.pcb is not None:
            self.pcbt.actualizrPcb(self.cpu.pcb)  # guarda el estado al pcbt
            self.planificador.agregarElemetoQReady(self.cpu.pcb)
            self.cpu.pcb = self.planificador.getProcess() # obtiene y asigna el sig programa (pcb)
            print("SE REALIZA EL  RESCHEDULE")
            
        elif self.planificador.getQReady().empty():
            #Si la QReady no contiene elementos genera una interrupcion  Signal.STOP
            self.cpu.pcb = None
            self.stop() #Corta la ejecucion del kernel y todos los demas threads existentes
        else:
            self.cpu.pcb  = self.planificador.getProcess() #obtiene del planificador el siguiente programa a ejecutar.
        
    #interrupcion EXIT #
    def exit(self):
        self.pcbt.eliminarPcb(self.cpu.pcb) # elimina el pcb de la tabla de PCBT
        self.memory.eliminarPrograma(self.cpu.pcb) # lo elimina de la memoria 
        self.cpu.pcb = None 
        self.reschedule() # una vez q elimina los datos del PCB reesqedulea
        
    #Este metodo tiene como finalidad obtener del planificador el programa con menos posibilidades de ejecucion#
    def getProgramaMenosUso(self):
        return self.planificador.getProgramaMenosUso()
    #Interrupcion SWAP#
    def swap(self):
        self.memory.ponerEnDisco(self)
    
    
    

        
    